package protocol

import (
	"bytes"
	"fmt"
	"math/big"
	"strconv"
	"time"

	"github.com/wanchain/go-mpc/p2p/discover"
)

const (
	MpcGPKLeader = iota + 0
	MpcGPKPeer
	MpcSignLeader
	MpcSignPeer
	MpcSignLeaderUni
	MpcSignPeerUni
)

const (
	MaxMpcContext = 5
)

const MaxMalice = 2

const (
	SK256Curve = iota + 0
	BN256Curve
)

const (
	ALGECDSA = iota + 0
	ALGSCHNORR
	ALGSCHNORR340
)

const MpcHeartBeatCycle = time.Second * 600 // 10 minutes
const MpcDelPeerCycle = time.Second * 10

//const MpcSntKnownCycle = MPCTimeOut + 5*time.Second
const MpcSntKnownCycle = 3 * time.Minute

const (
	StatusCode = iota + 10 // used by storeman protocol
	KeepaliveCode
	KeepaliveOkCode
	MPCError
	RequestMPC // ask for a new mpc Context
	RequestMPCBtc
	RequestMPCUni
	MPCMessage // get a message for a Context
	RequestMPCNonce
	KeepaliveCycle
	CheckAllPeerConnected
	BuildStoremanGroup
	AllPeersInfo
	GetPeersInfo
	NumberOfMessageCodes
	//todo should change back to 40s
	MPCTimeOut         = time.Second * 30
	MPCTimeOutValidate = time.Second * 30
	//MPCTimeOut = time.Second * 5
	PName   = "storeman"
	PVer    = uint64(1002)
	PVerStr = "10.0.2"
)

func GetMsgCodeStr(msgCode uint64) string {
	switch msgCode {
	case RequestMPC:
		return "RequestMPC"
	case RequestMPCBtc:
		return "RequestMPCBtc"
	case MPCError:
		return "MPCError"
	case MPCMessage:
		return "MPCMessage"
	default:
		return strconv.Itoa(int(msgCode))
	}
}

const (
	MpcPrivateShare        = "MpcPrivateShare"        // skShare
	MpcPrivateShareInverse = "MpcPrivateShareInverse" // skShare  uniMpc

	// R stage
	RSKSIJ     = "RSKSIJ"     // save sij with send index.
	RRcvedColl = "RRcvedColl" // scar number. received send index collection.(big.Int)

	RRcvedCollInter = "RRcvedCollInter" // received send index collection.(big.Int)

	RSkShare = "RSkShare" // rskShare   only self RSkShare
	RPkShare = "RPkShare" // rpkShare   rpkShare + "index" save rpkShare
	RCommit  = "RCommit"
	RPk      = "RPk" // RPk

	RSkShareInv = "RSkShareInverse" // rskShareInverse

	MpcPublicShare = "MpcPublicShare" // pkShare

	MpcContextResult = "MpcContextResult" // The final result of the context.

	PublicKeyResult = "PublicKeyResult" // gpk

	MpcM = "MpcM" // M
	MpcS = "MpcS" // S: s

	MpcExt       = "MpcExtern"    // extern
	MpcByApprove = "MpcByApprove" // by approve
	MpcCurve     = "MpcCurve"     // by approve

	MpcTxHash   = "MpcTxHash"
	MpcGpkBytes = "MpcGpkBytes"
	MPCAction   = "MPCAction"

	RPolyCMG  = "MPCRPolyCommitG" // node0's comment : RPolyCMG + "0" bytsvalue: polyValue values: sig of the polyValue
	RPolyCoff = "RPolyCoff"

	RSkErrNum   = "RSkErrNum"
	RSkErrInfos = "RSkErrInfos"

	SShareErrNum   = "SShareErrNum"
	SShareErrInfos = "SShareErrInfos"

	ROKIndex = "ROKIndex"
	RKOIndex = "RKOIndex"
	RNOIndex = "RNOIndex"

	SOKIndex = "SOKIndex"
	SKOIndex = "SKOIndex"
	SNOIndex = "SNOIndex"

	SSlshProof = "SSlshProof"
	RSlshProof = "RSlshProof"

	RSlshProofNum     = "RSlshProofNum"
	MPCSSlshProofNum  = "MPCSSlshProofNum"
	ApprovedMpcNumUni = "ApprovedMpcNumUni"

	FinishCollectUni = "FinishCollectUni"
)

//const MaxKnownMsgCnt = 2048  //todo restore
const MaxKnownMsgCnt = 20480 //todo restore

const (
	MpcApproving     = "MpcApproving"
	MpcApproved      = "MpcApproved"
	MpcApprovingKeys = "MpcApprovingKeys" // key : MpcApprovingKeys, value: array of the key of the data.

	MpcApprovingBtc     = "MpcApprovingBtc"
	MpcApprovedBtc      = "MpcApprovedBtc"
	MpcApprovingKeysBtc = "MpcApprovingKeysBtc" // key : MpcApprovingKeysBtc, value: array of the key of the data.

	MpcApprovingXrp     = "MpcApprovingXrp"
	MpcApprovedXrp      = "MpcApprovedXrp"
	MpcApprovingKeysXrp = "MpcApprovingKeysXrp" // key : MpcApprovingKeysXrp, value: array of the key of the data.

	MpcApprovingDot     = "MpcApprovingDot"
	MpcApprovedDot      = "MpcApprovedDot"
	MpcApprovingKeysDot = "MpcApprovingKeysDot" // key : MpcApprovingKeysBtc, value: array of the key of the data.

	MpcApprovingKeysUni = "MpcApprovingKeysUni" // key : MpcApprovingKeys, value: array of the key of the data.

)

type PeerInfo struct {
	PeerID discover.NodeID
	Seed   uint64
}
type SliceStoremanGroup []discover.NodeID

func (s SliceStoremanGroup) Len() int {
	return len(s)
}
func (s SliceStoremanGroup) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s SliceStoremanGroup) Less(i, j int) bool {
	return bytes.Compare(s[i][:], s[j][:]) < 0
}

type StepMessage struct {
	MsgCode   uint64 //message code
	PeerID    *discover.NodeID
	Data      []big.Int //message data
	BytesData [][]byte
	StepId    uint8
	//MpcErrMsg []byte
}

func (s *StepMessage) String() string {
	return fmt.Sprintf("StepMessage MsgCode:%v,PeerId:%v,len(Data):%d,len(BytesData):%d,StepId:%v",
		s.MsgCode, s.PeerID.SlimString(), len(s.Data), len(s.BytesData), s.StepId)
}

type GetMessageInterface interface {
	HandleMessage(*StepMessage) bool
}

// add for Btc begin
const (
	MpcAddress     = "MpcAddress"
	MpcTransaction = "MpcTransaction"
	MpcChainType   = "MpcChainType"
	MpcSignType    = "MpcSignType"
)

const (
	MpcSignLeaderBtc = iota + 4
	MpcSignPeerBtc
)

const (
	MpcPrivateKey            = "MpcPrivateKey"
	MpcSignA                 = "MpcSignA"
	MpcSignA0                = "MpcSignA0"
	MpcSignR                 = "MpcSignR"
	MpcSignR0                = "MpcSignR0"
	MpcSignB                 = "MpcSignB"
	MpcSignC                 = "MpcSignC"
	MpcSignARSeed            = "MpcSignARSeed"
	MpcSignARResult          = "MpcSignARResult"
	MpcTxSignSeed            = "MpcTxSignSeed"
	MpcTxSignResultR         = "MpcTxSignResultR"
	MpcTxSignResultV         = "MpcTxSignResultV"
	MpcTxSignResult          = "MpcTxSignResult"
	MpcSignAPoint            = "MpcSignAPoint"
	MpcChainID               = "MpcChainID"
	MPCActoin                = "MPCActoin"
	MPCSignedFrom            = "MPCSignedFrom"
	MpcStmAccType            = "MpcStmAccType"
	MpcPeerAlive             = "MpcPeerAlive"
	MpcPeerAliveIntersaction = "MpcPeerAliveIntersaction"
	MpcPeerAliveInterGroup   = "MpcPeerAliveInterGroup"
)

// for data save of BTC steps
const (
	JRJZDataA              = "JRJZDataA"
	JRJZDataR              = "JRJZDataR"
	JRJZDataB              = "JRJZDataB"
	JRJZDataC              = "JRJZDataC"
	JRJZData               = "JRJZData"
	JRJZCollection         = "JRJZCollection"
	BTCMpcPointCollectoin  = "BTCMpcPointCollectoin"
	BTCMpcPointData        = "BTCMpcPointData"
	MpcPeerRcvIntersaction = "MpcPeerRcvIntersaction"
	BTCRecvCol             = "BTCRecvCol"
)

func GetPreSetKeyArr(keySeed string, num int) []string {
	keyArr := []string{}
	for i := 0; i < num; i++ {
		keyArr = append(keyArr, keySeed+"_"+strconv.Itoa(i))
	}

	return keyArr
}

func GetPreSetKeyArrUni(keySeed string, num int) []string {
	keyArr := []string{}
	for i := 0; i < num; i++ {
		keyArr = append(keyArr, keySeed+strconv.Itoa(i))
	}

	return keyArr
}

// add for Btc end

func GetNilNodeId() *discover.NodeID {
	nodeId, _ := discover.HexID("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
	return &nodeId
}

func IsNilNodeId(nodeId *discover.NodeID) bool {
	nilNodeId := GetNilNodeId()
	if bytes.Equal(nodeId.Bytes(), nilNodeId.Bytes()) {
		return true
	}
	return false
}
